from PyQt5 import QtCore, QtGui, QtWidgets
from config import DIRECTION_UP, DIRECTION_RIGHT, DIRECTION_NONE, DIRECTION_LEFT, DIRECTION_DOWN
from config import UNIT_LENGTH, VIEW_HEIGHT, VIEW_WIDTH
from config import BASE_PERIOD, MOVE_COUNTER, BEAN_LIFE_COUNTER, EXPIRE_COUNTER, RANDOM_COUNTER, DATUM_MOVE_COUNTER
from config import TRICKS_PERIOD, PATH_PREFIX
from repaintsnake import RepaintSnake
from PyQt5.QtWidgets import QPushButton, QLabel, QMessageBox, QTableView, QDialog
from PyQt5.QtGui import QPainter, QColor, QBrush, QKeyEvent, QFont, QPen, QPalette, QPainterPath
from PyQt5.QtCore import Qt, QRect, QTimer
from expiremap import ExpireMap
from bean import Bean, RandomBean
from collision import collisionDetection
import random
from rankwidget import rankModel
import requests
import json
import winsound


class RepaintWidget(QtWidgets.QWidget):
    m_snake = None
    m_timer = None
    m_start_btn = None
    m_info_label = None
    m_score_widget = None

    m_move_counter = 0
    m_bean_life_counter = 0
    m_randomcount = 0  # 随机豆计数器，计数超出，则开始生成新一轮随机豆
    m_tricks_counter = -1  # 用来计算技能持续时间，-1表示未开启技能，0表示开启
    m_tricks_speed = 0  # 用来存储使用技能前的速度
    m_effect_counter = -1  # 效果豆的持续时间计数器，持续周期暂时与技能周期一致；-1表示未开启，≥0表示开启

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.resize(VIEW_WIDTH*UNIT_LENGTH, VIEW_HEIGHT*UNIT_LENGTH)
        self.m_snake = RepaintSnake(self)

        palette = self.palette()
        palette.setColor(QPalette.Background, QColor(121, 131, 44))
        self.setPalette(palette)
        self.setAutoFillBackground(True)

        self.m_start_btn = QPushButton(self)
        self.m_start_btn.setGeometry(
            (self.width()-80)/2, (self.height()-80)/2, 80, 80)
        self.m_start_btn.setCheckable(True)
        self.m_start_btn.setStyleSheet(
            "QPushButton{border-image:url("+ PATH_PREFIX +"static/pic/play.png);}")
        self.m_start_btn.clicked.connect(self.startOrPause)

        self.m_info_label = QLabel(self)
        self.m_info_label.setGeometry(
            0, 4*UNIT_LENGTH, self.width(), 4*UNIT_LENGTH)
        self.m_info_label.setAlignment(Qt.AlignCenter)
        font = QFont("微软雅黑")
        font.setPointSize(13)
        self.m_info_label.setFont(font)
        self.m_info_label.setStyleSheet("color:white;")
        self.m_info_label.setText(
            "请点击开始按钮或按空格键开始游戏！\n怒气值积满后，可点击【Q】键开启加速技能，或点击【W】键开启减速技能！\n请注意，积满一次怒气，只能开启一次技能。祝你好运！")

        self.m_timer = QTimer(self)
        self.m_timer.timeout.connect(self.timerOut)

        self.emap = ExpireMap() # Bean生命周期管理

        self.setFocus()

    def resetStatus(self): #重置状态，用于死完后
        m_move_counter = 0
        m_bean_life_counter = 0
        m_randomcount = 0  
        m_tricks_counter = -1  # 用来计算技能持续时间，-1表示未开启技能，0表示开启
        m_tricks_speed = 0  # 用来存储使用技能前的速度
        m_effect_counter = -1  # 效果豆的持续时间计数器，持续周期暂时与技能周期一致
        if self.m_score_widget != None:
            self.m_score_widget.switchNotify(key=None, status=False,
                                             type=self.m_snake.getEffectStatus())  # 提示特效结束
            self.m_snake.resumeStatus()
            self.m_effect_counter = -1
        MOVE_COUNTER = DATUM_MOVE_COUNTER
        self.m_tricks_counter = -1
        if self.m_score_widget != None:
            self.m_score_widget.switchNotify(key=None, status=False)

    def paintEvent(self, e): #重绘蛇
        painter = QPainter(self)
        painter.setBrush(QBrush(QColor(Qt.black)))
        pen = QPen(Qt.white)
        pen.setWidth(1)
        painter.setPen(pen)

        index_counter = 0
        for node in self.m_snake.getNodeList():
            rect = QRect((node.column-1)*UNIT_LENGTH, (node.row-1)
                         * UNIT_LENGTH, UNIT_LENGTH, UNIT_LENGTH)
            painter.drawRect(rect)
            if index_counter == 0:
                pen.setWidth(3)
                painter.setPen(pen)
                painter.drawPoint(rect.center())
                pen.setWidth(1)
                painter.setPen(pen)
            index_counter += 1

    def keyPressEvent(self, e): #按键事件处理
        key = e.key()

        def up():
            self.m_snake.setDirection(DIRECTION_UP)

        def down():
            self.m_snake.setDirection(DIRECTION_DOWN)

        def left():
            self.m_snake.setDirection(DIRECTION_LEFT)

        def right():
            self.m_snake.setDirection(DIRECTION_RIGHT)

        def space(): #start or pause
            self.switchInfo(1)
            self.startOrPause(not self.m_timer.isActive())
            self.m_start_btn.setChecked(not self.m_timer.isActive())

        def q(): #加速技能
            if self.m_score_widget != None and self.m_score_widget.getAnger() >= 100:
                self.speedTricks(True)
                self.m_score_widget.resetAnger()
                self.m_score_widget.switchNotify(key, True)

        def w(): #减速技能
            if self.m_score_widget != None and self.m_score_widget.getAnger() >= 100:
                self.speedTricks(False)
                self.m_score_widget.resetAnger()
                self.m_score_widget.switchNotify(key, True)

        switch = {
            Qt.Key_Up: up,
            Qt.Key_Down: down,
            Qt.Key_Left: left,
            Qt.Key_Right: right,
            Qt.Key_Space: space,
            Qt.Key_Q: q,
            Qt.Key_W: w, }
        try:
            switch.get(key)()
        except TypeError:
            print("没有这个按键的处理方法")

    # main timer time out slot
    def timerOut(self):
        self.m_move_counter += 1
        self.m_bean_life_counter += 1
        global MOVE_COUNTER
        global TRICKS_PERIOD

        if self.m_move_counter >= MOVE_COUNTER: #蛇移动计数器溢出
            self.m_snake.move()
            self.collisionDeath()
            self.collisionBean()
            self.update()
            self.m_move_counter = 0
            if self.m_snake.getEffectStatus() >= 0 and self.m_effect_counter == -1:  # 移动之后，判断是否刚吃到了效果豆
                self.m_effect_counter = 0  # 效果计数器置0，开始计数
                if self.m_score_widget != None:
                    self.m_score_widget.switchNotify(key=None, status=True,
                                                     type=self.m_snake.getEffectStatus())  # 提示反向特效开启

        global BEAN_LIFE_COUNTER
        if self.m_bean_life_counter >= BEAN_LIFE_COUNTER: #Bean生命计数器溢出
            self.m_bean_life_counter = 0

            items = self.emap.items()
            for item in list(items):
                item.periodTriggered()
                if item.getLifeValue() == 0:
                    self.emap.Remove(item)

        self.randombean() #随机生成Bean

        if self.m_tricks_counter >= 0:  # 开启速度技能，速度短暂使用恒定速度不随时间加快
            self.m_tricks_counter += 1

            if self.m_tricks_counter >= TRICKS_PERIOD:
                MOVE_COUNTER = self.m_tricks_speed
                self.m_tricks_counter = -1
                if self.m_score_widget != None:
                    self.m_score_widget.switchNotify(key=None, status=False)
        else:  # 未使用速度技能，则计算随积分一路增长的速度
            if self.m_score_widget != None:
                score = self.m_score_widget.getTotalScore()
                MOVE_COUNTER = DATUM_MOVE_COUNTER - score//4
                if MOVE_COUNTER <= 20:  # 速度最小值限制在20ms，可调整
                    MOVE_COUNTER = 20

        if self.m_effect_counter >= 0:  # 若已经吃到特效豆，则特效计数器便开始计数
            self.m_effect_counter += 1
            if self.m_effect_counter >= TRICKS_PERIOD:  # 特效持续时间到，重置相应状态
                if self.m_score_widget != None:
                    self.m_score_widget.switchNotify(key=None, status=False,
                                                     type=self.m_snake.getEffectStatus())  # 提示特效结束
                self.m_snake.resumeStatus()
                self.m_effect_counter = -1

    # start and pause slot
    def startOrPause(self, status):
        if status == False:  # pause
            self.m_timer.stop()
            self.m_start_btn.show()
            self.m_info_label.show()

            if self.m_snake.deathStatus():
                self.resetStatus()
                winsound.PlaySound(PATH_PREFIX + "static/audio/death.wav", winsound.SND_ASYNC)
            else:
                winsound.PlaySound(PATH_PREFIX + "static/audio/pause.wav",winsound.SND_ASYNC)

        else:  # start
            if self.m_snake.deathStatus():#刚死亡时，需要保留各项数据和显示，再次开启时，重置蛇的状态和积分
                self.m_snake.resetSnake()
                if self.m_score_widget != None:
                    self.m_score_widget.resetScore()
            self.m_timer.start(BASE_PERIOD)
            self.m_start_btn.hide()
            self.m_info_label.hide()
            winsound.PlaySound(PATH_PREFIX + "static/audio/start.wav", winsound.SND_ASYNC)

        self.setFocus(True)

    def randombean(self): #随机Bean方法
        if self.m_randomcount > 0:
            self.m_randomcount -= 1
            return
        # reset time random
        second = random.randint(1, 3)
        self.m_randomcount = (RANDOM_COUNTER / BASE_PERIOD)*second  # 随机时间刷新
        # random of bean's count.
        count = random.randint(0, 3) 
        for i in range(count): #计算生成随机数量、随机种类、随机生命周期的Bean
            r_x = random.randint(0, VIEW_WIDTH - 1)
            r_y = random.randint(0, VIEW_HEIGHT - 1)
            score = self.m_score_widget.getTotalScore()
            offset = score//200
            life = random.randint(5+offset, 15+offset)
            b = RandomBean(r_x*UNIT_LENGTH, r_y*UNIT_LENGTH,
                           UNIT_LENGTH, UNIT_LENGTH, life, self)
            b.show()
            self.emap.Insert(b, life)

    def collisionBean(self): #碰撞Bean的处理方法
        target = self.m_snake.getHead()
        items = self.emap.items()
        result = collisionDetection(target, items)
        for item in list(result):
            self.m_snake.collision(item)
            self.emap.Remove(item)
            if self.m_score_widget != None:
                self.m_score_widget.addScore(item.getLifeValue())

    def collisionDeath(self): #检测是否碰撞死亡
        target = self.m_snake.getHead()

        if target.x() < 0 or target.x() >= VIEW_WIDTH*UNIT_LENGTH or target.y() < 0 or target.y() >= VIEW_HEIGHT*UNIT_LENGTH:
            # death
            print('DEATH')
            self.switchInfo(2)
            self.m_snake.setDeathStatus(True)
            self.startOrPause(False)
            self.m_score_widget.gameOver(True)
            return

        items = self.m_snake.getBody()
        result = collisionDetection(target, items)
        if len(result) > 0:
            print('DEATH')
            self.switchInfo(2)
            self.m_snake.setDeathStatus(True)
            self.startOrPause(False)
            self.m_score_widget.gameOver(True)

    def switchInfo(self, type=1): #窗口中间切换暂停、开始、死亡的显示信息
        if type <= 0:
            return

        if type == 1:  # default，显示请点击开始字样
            self.m_info_label.setText(
                "请点击开始按钮或按空格键开始游戏！\n怒气值积满后，可点击【Q】键开启加速技能，或点击【W】键开启减速技能！\n请注意，积满一次怒气，只能开启一次技能。祝你好运！")
        elif type == 2:  # DEATH,显示已死亡，请重新开始字样
            self.m_info_label.setText("已死亡，请点击开始按钮或空格键重新开始！")

    def setScoreWidget(self, score_widget): #将积分窗口与重绘主窗口关联
        if score_widget == None:
            return
        self.m_score_widget = score_widget
        self.m_score_widget.uploadScore.connect(self.uploadScore)
        self.m_score_widget.checkRanking.connect(self.checkRanking)

    def speedTricks(self, status): #发动了速度相关技能
        global MOVE_COUNTER
        self.m_tricks_counter = 0
        self.m_tricks_speed = MOVE_COUNTER
        if status:  # speed up
            MOVE_COUNTER /= 2
        else:  # speed down
            MOVE_COUNTER *= 2

    def uploadScore(self, score, name):#确认上传积分
        url = None
        if url == None:
            print("接口为空，请确认上传接口！")
            return
        r = requests.get(url % (name, score))
        try:
            result = json.loads(r.text)
            # print(result)
            if result.get('code') == 0:
                info = "用户名：" + name + " - 总积分：" + str(score) + "  已上传成功！"
                QMessageBox.information(self, "积分上传", info, QMessageBox.Yes)
            else:
                QMessageBox.information(self, "积分上传", "上传失败", QMessageBox.Yes)
        except AttributeError:
            print("upload error...")

        self.setFocus(True)  # 不能去掉，并且要放在最后，防止窗口事件被夺走

    def checkRanking(self):  # 查看排行榜
        print('查看榜单')


        rank_widget = QDialog(self)
        rank_widget.setWindowTitle("Top 20 排行榜")
        rank_widget.resize(VIEW_WIDTH*UNIT_LENGTH/2, VIEW_HEIGHT*UNIT_LENGTH)
        rank_widget.setStyleSheet("text-align:center;")
        tabview = QTableView(rank_widget)
        model = rankModel()
        tabview.setModel(model)
        tabview.resize(VIEW_WIDTH*UNIT_LENGTH/2, VIEW_HEIGHT*UNIT_LENGTH)
        tabview.show()

        #临时数据，如果有后台的话，请通过接口获取后台数据，并构造如下格式datas
        datas = [
            [1,"yang", 100], #依次是排名、姓名、积分
            [1,"yang2", 90],
        ]

        model.loadData(datas)

        rank_widget.show()
        rank_widget.exec()

        self.setFocus(True)  # 不能去掉，并且要放在最后，防止窗口事件被夺走
        pass
